/*************************************************************************
	> File Name: client.c
	> Author: Maureen 
	> Mail: 1437867946@qq.com 
	> Created Time: Wed 12 May 2021 05:32:28 PM CST
 ************************************************************************/

#include "head.h"

char server_ip[20] = {0};
int server_port = 0;
char *conf = "./football.conf";
//char *conf = "./myfootball.conf";
int sockfd = -1;
struct Map court;
struct Bpoint ball;
struct FootBallMsg chat_msg;
struct FootBallMsg ctl_msg;
int show_name = 0;
int message_num = 0;

void logout(int signum) {
    struct FootBallMsg msg;
    msg.type = FT_FIN;
    send(sockfd, (void *)&msg, sizeof(msg), 0);
    DBG(YELLOW"[Client]"NONE" : Client send offline message to Server.\n");
    sleep(1);
    close(sockfd);
    DBG(YELLOW"[Client]"NONE" : Bye!\n");
    exit(0);
}

int main(int argc, char **argv) {
    int opt;
    struct LoginRequest request;
    struct LoginResponse response;
    pthread_t recv_t, draw_t;
    bzero(&request, sizeof(request));
    bzero(&response, sizeof(response));
    bzero(&chat_msg, sizeof(struct FootBallMsg));
    bzero(&ctl_msg, sizeof(struct FootBallMsg));
    chat_msg.type = FT_WALL;
    ctl_msg.type = FT_CTL;
    ctl_msg.ctl.action = ACTION_DFT;
    request.team = -1;

    while ((opt = getopt(argc, argv, "h:p:t:m:n:")) != -1) {
        switch (opt) {
            case 't':
                request.team = atoi(optarg);
                break;
            case 'h':
                strcpy(server_ip, optarg);
                break;
            case 'p':
                server_port = atoi(optarg);
                break;
            case 'm':
                strcpy(request.msg, optarg);
                break;
            case 'n':
                strcpy(request.name, optarg);
            default:
                fprintf(stderr, "Usgae : %s -h ip -p port -t team -m msg.\n", argv[0]);
                exit(1);
        }
    }

    //判断选项是否合法,若不合法从配置文件读取信息
    if (!server_port) server_port = atoi(get_value(conf, "serverport"));
    if (!strlen(server_ip)) strcpy(server_ip, get_value(conf, "serverip"));
    if (request.team == -1) request.team = atoi(get_value(conf, "team"));
    if (!strlen(request.msg)) strcpy(request.msg, get_value(conf, "loginmsg"));
    if (!strlen(request.name)) strcpy(request.name, get_value(conf, "name"));


    DBG(GREEN"INFO"NONE" : client start.\n");
    DBG(GREEN"=================Conf Info:===================\n"NONE);
    DBG("server_ip = %s\n", server_ip);
    DBG("server_port = %d\n", server_port);
    DBG("team = %d\n", request.team);
    DBG("name = %s\n", request.name);
    DBG("msg = %s\n", request.msg);
    DBG(GREEN"==============================================\n"NONE);

    signal(SIGINT, logout); //接收ctrl+c的键盘信号

   
    //服务端信息配置
    struct sockaddr_in server;
    server.sin_family = AF_INET; //ipv4
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip);

    socklen_t len = sizeof(server);

    //创建udp socket
    if ((sockfd = socket_udp()) < 0) {
        perror("socket_udp");
        exit(1);
    }
    DBG(YELLOW"sockfd = %d\n"NONE, sockfd);

    
    //发送登录请求给服务端
    if (sendto(sockfd, (void *)&request, sizeof(request), 0, (struct sockaddr *)&server, len) < 0) { //sendto相比send，多了个地址，因为udp没有建立连接，所以要将地址传入
        perror("sendto");
        exit(1);
    }

    //select实现的5s的定时器,判断服务器是否在线
    fd_set set;
    FD_ZERO(&set);
    FD_SET(sockfd, &set);
    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    int retval = select(sockfd + 1, &set, NULL, NULL, &tv); //select等待5s，5s内是否收到服务端的回复消息,如果收不到则说明服务不在线
    if (retval < 0) {
        perror("select");
        exit(1);
    } else if (retval) {
        int ret = recvfrom(sockfd, (void *)&response, sizeof(response), 0, (struct sockaddr *)&server, &len); //接收服务端回复的消息
        if (ret != sizeof(response) || response.type) {
            DBG(RED"Error"NONE"The Game Server refused your login request. More message:%s\n", response.msg);
            exit(1);
        }
    } else {
        DBG(RED"Error"NONE" : The Game Server is out of service.\n");
        exit(1);
    }
    DBG(GREEN"Server"NONE" : %s\n", response.msg);

    connect(sockfd, (struct sockaddr *)&server, len); //连接服务端，建立伪连接。此处的sockfd对应的就是服务端,四元组
    
    pthread_create(&recv_t, NULL, client_recv, NULL); //使用recv_t线程接收服务端的消息


    while (1) {
        int c = getchar();
        DBG("Pressed: c = %d\n", c);
        switch (c) {
            case 'm':
                //测试发送FT_MSG普通信息给Server 
                chat_msg.type = FT_MSG;
                strcpy(chat_msg.msg, "It's a test msg.");
                send(sockfd, (void *)&chat_msg, sizeof(chat_msg), 0);
                break;
            case 'a':
                ctl_msg.ctl.dirx -= 1;
                break;
            case 'd':
                ctl_msg.ctl.dirx += 1;
                break;
            case 'w':
                ctl_msg.ctl.diry -= 1;
                break;
            case 's':
                ctl_msg.ctl.diry += 1;
                break;
            case 27: //esc 
                //发送球员位置移动信息给Server
                DBG(BLUE"[Client] : Send ctl msg.\n"NONE);
                if (ctl_msg.ctl.dirx || ctl_msg.ctl.diry) 
                    send(sockfd, (void *)&ctl_msg, sizeof(ctl_msg), 0);
                ctl_msg.ctl.dirx = ctl_msg.ctl.diry = 0;
                break;
            case 'k': {
                //发送踢球指令给Server
                struct FootBallMsg msg;
                bzero(&msg, sizeof(msg));
                msg.type = FT_CTL;
                msg.ctl.action = ACTION_KICK;
                msg.ctl.strength = 1;
                send(sockfd, (void *)&msg, sizeof(msg), 0);
                DBG(BLUE"[Client] : Send ACTION_KICK msg to server.\n"NONE);
                break;
            }
            case 'j': {
                //发送停球指令给Server
                struct FootBallMsg msg;
                bzero(&msg, sizeof(msg));
                msg.type = FT_CTL;
                msg.ctl.action = ACTION_STOP;
                send(sockfd, (void *)&msg, sizeof(msg), 0);
                DBG(BLUE"[Client] : Send ACTION_STOP msg to server.\n"NONE);
                break;
            }
            case 'l': {
                //发送待求指令给server
                struct FootBallMsg msg;
                bzero(&msg, sizeof(msg));
                msg.type = FT_CTL;
                msg.ctl.action = ACTION_CARRY;
                DBG(BLUE"[Client] : Send ACTION_CARRY msg to server.\n"NONE);
                send(sockfd, (void *)&msg, sizeof(msg), 0);
                break;
            }
            default:
                break;
        }
        
    }

    return 0;
}
